//======================================================================================================================
// 2007 Copyright Mystic Lake Software
// Based on work Copyright 2002 Phil Karn, KA9Q
//
// This file is part of The Ham Radio Network.
//
// The Ham Radio Network is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// The Ham Radio Network is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//======================================================================================================================
//
//     Author: Rudyard Merriam
//
//      $Id: rs.cpp 3 2007-09-19 04:25:45Z k5rud@arrl.net $
//
//======================================================================================================================
//
#include <algorithm>
#include <bitset>
#include <fstream>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
using namespace std;

#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#include "ReedSolomon255223.h"
//----------------------------------------------------------------------------------------------------------------------
typedef istreambuf_iterator<char> char_in;
typedef ostreambuf_iterator<char> char_out;

size_t b_cnt = 0;
//==========================================================================================================================
class rs_functor
{
public:
    rs_functor() { init(); }
    rs_functor(const rs_functor& rsf) { init(); }
    virtual ~rs_functor() = 0 { };

    virtual void operator()(const char& ch) = 0 { };

protected:
    void init() { mCnt = 0; }
    virtual void flush() = 0;

    mutable size_t mCnt;
    byte mBlock[ReedSolomon255223::NN];
    ReedSolomon255223 mRs;
};
//----------------------------------------------------------------------------------------------------------------------
class encode_functor : public rs_functor
{
public:
    encode_functor() { }
    encode_functor(const encode_functor& ef) : rs_functor(ef) { }
    virtual ~encode_functor() 
    {
        if (mCnt) 
        {
//            cerr << "\t" << b_cnt++ << endl;
            flush(); 
        }
        mCnt = 0;
    }

    virtual void operator()(const char& ch)
    {
        if (mCnt == ReedSolomon255223::NN - ReedSolomon255223::NROOTS)
        {
//            cerr << b_cnt++ << endl;
            mRs.encode(mBlock, &mBlock[ReedSolomon255223::NN - ReedSolomon255223::NROOTS]);
            copy(mBlock, &mBlock[ReedSolomon255223::NN], char_out(cout));
            init();
        }
        mBlock[mCnt++] = static_cast<unsigned char>(ch);
    }

protected:
    virtual void flush()
    {
        if (mCnt != ReedSolomon255223::NN - ReedSolomon255223::NROOTS)
        {
            memset(&mBlock[mCnt+1], 0, ReedSolomon255223::NN);   // zero fill remaining buffer
        }
        mRs.encode(mBlock, &mBlock[ReedSolomon255223::NN - ReedSolomon255223::NROOTS]);

        // send only data and roots, omitting zero padding
        copy(mBlock, &mBlock[mCnt], char_out(cout));
        copy(&mBlock[ReedSolomon255223::NN - ReedSolomon255223::NROOTS], &mBlock[ReedSolomon255223::NN], char_out(cout));
    }

};
//----------------------------------------------------------------------------------------------------------------------
class decode_functor : public rs_functor
{
public:
    decode_functor() { }
    decode_functor(const decode_functor& df) : rs_functor(df) { }
    virtual ~decode_functor()
    {
        if (mCnt) 
        {
//            cerr << "\t" << b_cnt++ << endl;
            flush(); 
        }
        mCnt = 0;
    }

    virtual void operator()(const char& ch)
    {
        if (mCnt == ReedSolomon255223::NN)
        { 
//            cerr << b_cnt++ << endl;
            int derrlocs[ReedSolomon255223::NROOTS];

            mRs.decode(mBlock, derrlocs, 0);
            copy(mBlock, &mBlock[ReedSolomon255223::NN - ReedSolomon255223::NROOTS], char_out(cout));
            init();
        }
        mBlock[mCnt++] = static_cast<unsigned char>(ch);
    }

protected:

    virtual void flush()
    {
        if (mCnt < ReedSolomon255223::NN && mCnt != 0)
        {
            // move roots to top of buffer then zero fill
            memcpy(&mBlock[mCnt - ReedSolomon255223::NROOTS], &mBlock[ReedSolomon255223::NN - ReedSolomon255223::NROOTS], ReedSolomon255223::NROOTS);
            memset(&mBlock[mCnt - ReedSolomon255223::NROOTS], 0, ReedSolomon255223::NN - ReedSolomon255223::NROOTS);
        }

        int derrlocs[ReedSolomon255223::NROOTS];

        mRs.decode(mBlock, derrlocs, 0);
        copy(mBlock, &mBlock[mCnt - ReedSolomon255223::NROOTS], char_out(cout));
    }
};
//==========================================================================================================================
int main(int argc, char *argv[ ])
{
    if (argc == 2)
    {
        if (argv[1][0] == 'E')
        {
            _setmode(_fileno(stdin), _O_BINARY);
            _setmode(_fileno(stdout), _O_BINARY);

            for_each((char_in(cin)), char_in(), encode_functor());
            cout.flush();
        }
        else if (argv[1][0] == 'D')
        {
            _setmode(_fileno(stdin), _O_BINARY);
            _setmode(_fileno(stdout), _O_BINARY);

            for_each((char_in(cin)), char_in(), decode_functor());
            cout.flush();
        }
    }
    else
    {
        cerr << "(E)ncode or (D)ecode option missing" << endl;
    }
}
